from codeflow.reviewer.models import Changeset, Review, ChangesetReview
from codeflow.reviewer.models import ChangesetReviewComment
from codeflow.reviewer.models import InlineChangesetReviewComment
from codeflow import settings
from django.views.generic.list_detail import object_list, object_detail
from django.contrib.auth.decorators import login_required
from django.shortcuts import render_to_response
from django.http import HttpResponseRedirect
from django.template import RequestContext
from django.db.models import Q
from django import newforms as forms
from django.core.mail import send_mail

def show_changeset(request, scm_id):
    cset, created = Changeset.objects.get_or_create(scm_id=scm_id)
    return render_to_response('codeflow/reviewer/changeset.html',
                              {'cset': cset},
                              context_instance=RequestContext(request))
show_changeset = login_required(show_changeset)


class ChangesetReviewRequestData(object):
    """
    Data coming from requests that intends to modify or create a changeset review.

    This class takes care of extracting and validating the data coming from the
    HttpRequest object.

    (I suppose that Django forms were supossed to do this work, but they don't seem
     to support lists of values)
    """
    def __init__(self, request):
        # TODO: Validate data!
        self.status = ''
        if request.POST.has_key('review_passed'):
            self.status = Review.REVIEW_PASSED
        elif request.POST.has_key('review_failed'):
            self.status = Review.REVIEW_FAILED    
        self.general_comment = request.POST['general_comment']
        self.cset_scm_id = request.POST['changeset_scm_id']
        self.comment_paths = request.POST.getlist('comment_path')
        self.comment_which_revisions = request.POST.getlist('comment_whichRevision')
        self.comment_line_numbers = request.POST.getlist('comment_lineno')
        self.comment_texts = request.POST.getlist('comment_text')        
    def inline_comments(self):
        """
        Returns a sequence of tuples with the inline comments info. The tuples are
        of the form (path, wich_revision, line_number, text)
        """
        return zip(self.comment_paths,
                   self.comment_which_revisions,
                   self.comment_line_numbers,
                   self.comment_texts)
        
new_changesest_review_mail = """Hi,
%(user_name)s has reviewed your code from changeset %(changeset_id)s:

%(general_comment)s

The current status of the review is '%(review_status)s'. To see more
details (such as inline comments which are not included on this mail),
please visit %(review_url)s ."""


def create_changeset_review(request):
    data = ChangesetReviewRequestData(request)
    review = Review.objects.create(kind=Review.CHANGESET_REVIEW,
                                   status=data.status, author=request.user)
    cset = Changeset.objects.get(scm_id=data.cset_scm_id)
    cset_review = ChangesetReview.objects.create(review=review, changeset=cset)
    # Comment:
    ChangesetReviewComment.objects.create(changeset_review=cset_review,
                                          text=data.general_comment,
                                          author=request.user)
    # Inline comments:
    for path, which_revision, line_number, text in data.inline_comments():
        InlineChangesetReviewComment.objects.create(
            changeset_review=cset_review, file_path=path,
            which_revision=which_revision,  
            line_number=line_number, text=text, author=request.user)
    # Mail the changeset author, if we can get his email address
    if cset.scm_user.user and cset.scm_user.user.email:
        message = new_changesest_review_mail % \
                  {'user_name': request.user.username,
                   'changeset_id': data.cset_scm_id,
                   'general_comment': data.general_comment,
                   'review_status': data.status,
                   'review_url': (settings.CODEFLOW_BASE_URL +
                                  '/review/%s/' % review.id)}
        send_mail('New Code Review', message, settings.CODEFLOW_MAIL_FROM,
                  [cset.scm_user.user.email], fail_silently=False)
        
    return HttpResponseRedirect('/review/%s/' % review.id)

create_changeset_review = login_required(create_changeset_review)

edit_changeset_review_mail = """Hi,

%(user_name)s has replied the review %(review_id)s (of changeset %(changeset_id)s):

%(general_comment)s

The current status of the review is '%(review_status)s'. To see more
details (such as inline comments which are not included on this mail),
please visit %(review_url)s ."""

def show_review(request, review_id):
    review = Review.objects.get(id=int(review_id))
    if review.kind == Review.CHANGESET_REVIEW: 
        cset_reviews = review.changeset_reviews.all()
        assert len(cset_reviews) == 1, \
               "A review SHOULD have one and only one changeset review object"
        cset_review = cset_reviews[0]
        cset = cset_review.changeset       
        if request.method == 'POST':
            data = ChangesetReviewRequestData(request)
            if request.user == review.author:
                review.status = data.status
                review.save()
            if data.general_comment.strip() != '':
                ChangesetReviewComment.objects.create(changeset_review=cset_review,
                                                      text=data.general_comment,
                                                      author=request.user)
            for path, which_revision, line_number, text in data.inline_comments():
                InlineChangesetReviewComment.objects.create(
                    changeset_review=cset_review, file_path=path,
                    which_revision=which_revision, line_number=line_number,
                    text=text, author=request.user)
            # Mail everyone involved on the review
            recipients = [u.email for u in cset_review.involved_users()
                          if u.email is not None]
            if len(recipients) > 0:
                message = edit_changeset_review_mail % \
                          {'user_name': request.user.username,
                           'review_id': review.id,
                           'changeset_id': data.cset_scm_id,
                           'general_comment': data.general_comment,
                           'review_status': data.status,
                           'review_url': (settings.CODEFLOW_BASE_URL +
                                          '/review/%s/' % review.id)}
                send_mail('New Comment in Review %s' % review.id,
                          message, settings.CODEFLOW_MAIL_FROM, recipients,
                          fail_silently=False)
            return HttpResponseRedirect('/review/%s/' % review_id)
        elif request.method == 'GET':
            return render_to_response('codeflow/reviewer/changeset.html',
                                      {'cset': cset,
                                       'review': review,
                                       'cset_review': cset_review},
                                      context_instance=RequestContext(request))
                                                 
def search_review(request):
    class SearchForm(forms.Form):
        text = forms.CharField()
    if request.method == 'GET':
        return render_to_response('codeflow/reviewer/search.html',
                                  {'form': SearchForm()},
                                  context_instance=RequestContext(request))
    elif request.method == 'POST':
        search_query = request['query']
        kwords = []
        cset_id = None
        for token in search_query.strip().split(): # FIXME: Implement grouping
            if ':' in token and token.split(':')[0] in ('cset', 'changeset'):
                # search by changeset id 
                cset_id = token.split(':')[1]
            else:
                kwords.append(token)
        q = Review.objects.all()
        if cset_id is not None:
            q = q.filter(changeset_reviews__changeset_scm_id=cset_id)
        if len(kwords) > 0:
            qkwords = (Q(changeset_reviews__comments__text__icontains=kwords[0])
                       |
                       Q(changeset_reviews__changeset__message__icontains=kwords[0])
                       # FIXME: If we search into inline comments, reviews without
                       #        them are discarded because django always does
                       #        INNER JOINS
                       #        (ref: http://code.djangoproject.com/ticket/3592)
                       # |
                       # Q(changeset_reviews__inline_comments__text__icontains=kwords[0])
                       )
        
            for kword in kwords[1:]:
                qkwords = (qkwords
                           |
                           Q(changeset_reviews__comments__text__icontains=kword)
                           |
                           Q(changeset_reviews__changeset__message__icontains=kword)
                           # Same FIXME as above
                           #|
                           #Q(changeset_reviews__inline_comments__text__icontains=kword)
                           )
            q = q.filter(qkwords)
        return render_to_response('codeflow/reviewer/search_results.html',
                                  {'results': q},
                                  context_instance=RequestContext(request))
                                
                
        
        
def inbox(request):
    reviews_of_user_changesets = ChangesetReview.objects.filter(
        changeset__scm_user__user=request.user).order_by('-id')#.order_by('-review.date')
    changeset_reviews_by_the_user = ChangesetReview.objects.filter(
        review__author=request.user).order_by('-id')#.order_by('-review.date')
    print reviews_of_user_changesets
    print changeset_reviews_by_the_user
    # TODO: Include changeset reviews commented by the user
    return render_to_response(
        "codeflow/reviewer/inbox.html",
        {'reviews_of_user_changesets': reviews_of_user_changesets,
         'changeset_reviews_by_the_user': changeset_reviews_by_the_user},
        context_instance=RequestContext(request))
inbox = login_required(inbox)
                       
def home(request):
    return HttpResponseRedirect("/review/inbox/")
